package net.studio.simpletetris.control;

import java.util.ArrayList;
import java.util.List;

import net.studio.simpletetris.intf.IBlock;
import net.studio.simpletetris.intf.IField;
import net.studio.simpletetris.intf.IFigure;

public class Field implements IField {

	protected List<IBlock> blocks = null;
	protected byte[][] structure = null;
	protected IFigure figure = null;

	protected int cols = 0;
	protected int rows = 0;
	protected int color = 0;
	
	public Field(int rows, int cols, int color) {
		setRows(rows);
		setCols(cols);
		setColor(color);
		this.blocks = new ArrayList<IBlock>();
		this.structure = new byte[getCols()][getRows()];
	}

	public int getCols() {
		return this.cols;
	}

	public void setCols(int value) {
		if (this.cols != value) {
			this.cols = value;
			initStructure(rows, cols);
		}
	}

	public int getRows() {
		return this.rows;
	}

	public void setRows(int value) {
		if (this.rows != value) {
			this.rows = value;
			initStructure(rows, cols);
		}
	}

	public int getColor() {
		return this.color;
	}

	public void setColor(int color) {
		this.color = color;
	}

	synchronized public IBlock addBlock(int x, int y, int color) {
		if (x < 0 || y < 0) 
			return null;
		structure[x][y] = 1;
		Block block = new Block(x, y, color);
		blocks.add(block);
		return block;
	}
	
	public boolean validateIntersection(IFigure figure, boolean extend) {
		boolean result = false;
		if (extend) {
			if (validateFieldExtendBorder(figure) || validateBlockIntersection(figure)) {
				result = true;
			}
		} else {
			if (validateFieldBorder(figure) || validateBlockIntersection(figure)) {
				result = true;
			}
		}
		return result;
	}

	private boolean validateFieldBorder(IFigure figure) {
		int x, y;

		for (int i = 0; i < figure.getBlockCount(); i++) {

			x = figure.getX() + figure.getBlock(i).getX();
			y = figure.getY() + figure.getBlock(i).getY();

			if (!((x >= 0 && x < this.getCols()) && (y >= 0 && y < this.getRows()))) {
				return true;
			}
		}
		
		return false;
	}

	private boolean validateFieldExtendBorder(IFigure figure) {
		int x, y;

		for (int i = 0; i < figure.getBlockCount(); i++) {

			x = figure.getX() + figure.getBlock(i).getX();
			y = figure.getY() + figure.getBlock(i).getY();

			if (!((x >= 0 && x < this.getCols()) && (y >= -figure.getBlockSize() && y < this.getRows()))) {
				return true;
			}
		}
		
		return false;
	}

	private boolean validateBlockIntersection(IFigure figure) {
		int x, y;

		for (int i = 0; i < figure.getBlockCount(); i++) {
			IBlock block = figure.getBlock(i);
			x = block.getX() + figure.getX();
			y = block.getY() + figure.getY();

			if (x >= 0 && x < this.structure.length) {
				if (y >= 0 && y < this.structure[x].length) {
					if (this.structure[x][y] != 0) { 
						return true;
					}
				}
			}
		}

		return false;
	}

	public void fixFigureBlocks(IFigure figure) {
		IBlock block = null;

		int x, y;

		for (int i = 0; i < figure.getBlockCount(); i++) {
			block = figure.getBlock(i);

			x = figure.getX() + block.getX();
			y = figure.getY() + block.getY();

			this.addBlock(x, y, block.getColor());
		}
	}

	public int checkLinesToDrop() {
		int result = 0;

		if (blocks.size() >= this.getCols()) {
			int[] counter = new int[this.getRows()];

			for (int i = 0; i < blocks.size(); i++) {
				counter[blocks.get(i).getY()] += 1;
				if (counter[blocks.get(i).getY()] == this.getCols()) {
					int line = blocks.get(i).getY();
					this.dropLine(line);
					this.moveBlocksDown(line);
					result++;
				}
			}
		}
		return result;
	}

	public void dropLine(int line) {
		List<IBlock> tmp = new ArrayList<IBlock>();
		for (int i = 0; i < blocks.size(); i++) {
			int x = blocks.get(i).getX();
			int y = blocks.get(i).getY();
			if (y == line) {
				structure[x][y] = 0;
			} else {
				tmp.add(blocks.get(i));
			}
		}
		blocks = tmp;
	}

	synchronized public void moveBlocksDown(int line) {
		for (int x = 0; x < getCols(); x++) {
			for (int y = 0; y < getRows(); y++) {
				structure[x][y] = 0;
			}
		}

		for (int i = 0; i < blocks.size(); i++) {
			int x = blocks.get(i).getX();
			int y = blocks.get(i).getY();
			if (y < line) {
				y++;
				blocks.get(i).setY(y);
			}
			structure[x][y] = 1;
		}
	}
	
	public int countLine(int line) {
		int result = 0;
		for (int i = 0; i < getCols(); i++) {
			if (structure[i][line] != 0)
				result++;
		}
		
		return result;
	}
	
	public void initStructure(int rows, int cols) {
		byte[][] tempStructure = new byte[cols][rows];
		if (structure == null) {
			structure = tempStructure;
			return;
		}
		for (int x = 0; x < Math.min(structure.length, tempStructure.length); x++) {
			for (int y = 0; y < Math.min(structure[x].length, tempStructure[x].length); y++) {
				tempStructure[x][y] = structure[x][y];
			}
		}
		structure = tempStructure;
	}

	@Override
	synchronized public void removeBlock(int index) {
		int x = this.blocks.get(index).getX();
		int y = this.blocks.get(index).getY();
		this.blocks.remove(index);
		if (x < 0 || y < 0) return;
		structure[x][y] = 0;
	}

	@Override
	synchronized public IBlock getBlock(int index) {
		if (index < getBlockCount())
			return this.blocks.get(index);
		return null;
	}

	@Override
	synchronized public int getBlockCount() {
		return this.blocks.size();
	}

	@Override
	synchronized public IFigure getFigure() {
		return figure;
	}

	@Override
	synchronized public void setFigure(IFigure value) {
		figure = value;
	}

}
